package com.guojiang.guopicturebackend.manager.upload;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.RandomUtil;
import com.guojiang.guopicturebackend.config.CosClientConfig;
import com.guojiang.guopicturebackend.exception.BusinessException;
import com.guojiang.guopicturebackend.exception.ErrorCode;
import com.guojiang.guopicturebackend.exception.ThrowUtils;
import com.guojiang.guopicturebackend.manager.CosManager;
import com.guojiang.guopicturebackend.model.dto.file.UploadPictureResult;
import com.guojiang.guopicturebackend.model.entity.Picture;
import com.guojiang.guopicturebackend.service.PictureService;
import com.guojiang.guopicturebackend.utils.ColorTransformUtils;
import com.qcloud.cos.model.PutObjectResult;
import com.qcloud.cos.model.ciModel.persistence.CIObject;
import com.qcloud.cos.model.ciModel.persistence.ImageInfo;
import com.qcloud.cos.model.ciModel.persistence.ProcessResults;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Lazy;

import javax.annotation.Resource;
import java.io.File;
import java.util.Arrays;
import java.util.Date;
import java.util.List;

/**
 * （图片）文件处理模板
 */
@Slf4j
public abstract class PictureUploadTemplate {

    @Resource
    private CosClientConfig cosClientConfig;

    /**
     * 引入写好的对象存储通用类
     */
    @Resource
    private CosManager cosManager;

    @Lazy
    @Resource
    private PictureService pictureService;

    /**
     * 上传图片
     *
     * @param inputSource      文件
     * @param uploadPathPrefix 上传路径前缀
     * @return
     */
    public UploadPictureResult uploadPicture(Object inputSource, String uploadPathPrefix) {
        // 1. 校验图片大小、格式（可以自定义添加其他逻辑）
        validPicture(inputSource);
        // 2. 获取图片上传地址（格式化时间 + 随机Id + 文件名）
        // 自己拼接文件名，可以增加安全性
        String uuid = RandomUtil.randomString(16);
        String originalFilename = getOriginFilename(inputSource);
        // 拼接文件名称
        String uploadPictureName = String.format("%s_%s.%s",
                DateUtil.formatDate(new Date()), uuid, FileUtil.getSuffix(originalFilename));
        // 拼接最终的文件上传路径
        String uploadPath = String.format("/%s/%s", uploadPathPrefix, uploadPictureName);
        // 3. 上传图片文件
        // 新建临时文件
        File file = null;
        try {
            // 创建临时文件
            file = File.createTempFile(uploadPath, null);
            // 处理文件来源
            processFile(inputSource, file);
            // 4. 上传图片到对象存储
            PutObjectResult putObjectResult = cosManager.putPictureObject(uploadPath, file);
            // 5. 获取图片上传后的原始图片信息对象，以便封装返回结果
            ImageInfo imageInfo = putObjectResult.getCiUploadResult().getOriginalInfo().getImageInfo();
            // 6. 获取图片压缩、缩略处理结果对象
            ProcessResults processResults = putObjectResult.getCiUploadResult().getProcessResults();
            List<CIObject> objectList = processResults.getObjectList();
            if (CollUtil.isNotEmpty(objectList)) {
                // 获得压缩后的图片
                CIObject compressedCiObject = objectList.get(0);
                // 缩略图默认等于压缩图（即使不进行缩略图操作，也能保证首页展示缩略图功能正常）
                CIObject thumbnailCiObject = compressedCiObject;
                if (objectList.size() > 1) {
                    // 获得缩略图
                    thumbnailCiObject = objectList.get(1);
                }
                // 封装返回结果
                return buildResult(originalFilename, compressedCiObject, thumbnailCiObject, uploadPath, imageInfo, file);
            }
            return buildResult(originalFilename, file, uploadPath, imageInfo);
        } catch (Exception e) {
            log.error("图片上传到对象存储失败", e);
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "上传失败");
        } finally {
            // 6. 清理临时文件
            this.deleteTempFile(file);
        }
    }

    /**
     * 校验输入源（本地图片文件 或 文件URL）
     *
     * @param inputSource
     */
    protected abstract void validPicture(Object inputSource);

    /**
     * 获取输入源的原始文件名
     *
     * @param inputSource
     * @return
     */
    protected abstract String getOriginFilename(Object inputSource);

    /**
     * 处理输入源，生成本地临时文件
     *
     * @param inputSource
     */
    protected abstract void processFile(Object inputSource, File file) throws Exception;

    /**
     * 封装返回结果
     *
     * @param originalFilename    原始图片名
     * @param file                原图文件
     * @param originUrlUploadPath 原图上传路径
     * @param imageInfo           对象存储返回的图片信息
     * @return
     */
    private UploadPictureResult buildResult(String originalFilename, File file, String originUrlUploadPath, ImageInfo imageInfo) {
        // 计算宽高
        int picWidth = imageInfo.getWidth();
        int picHeight = imageInfo.getHeight();
        double picScale = NumberUtil.round(picWidth * 1.0 / picHeight, 2).doubleValue();
        // 封装返回结果
        UploadPictureResult uploadPictureResult = new UploadPictureResult();
        uploadPictureResult.setOriginUrl(cosClientConfig.getHost() + "/" + originUrlUploadPath);
        uploadPictureResult.setPicName(FileUtil.mainName(originalFilename));
        uploadPictureResult.setPicSize(FileUtil.size(file));
        uploadPictureResult.setPicWidth(picWidth);
        uploadPictureResult.setPicHeight(picHeight);
        boolean checkPicScaleResult = checkPicScale(picScale, uploadPictureResult);
        ThrowUtils.throwIf(checkPicScaleResult, ErrorCode.PARAMS_ERROR, "图片宽高比非法");
        uploadPictureResult.setPicScale(picScale);
        uploadPictureResult.setPicFormat(imageInfo.getFormat());
        String standardColor = ColorTransformUtils.getStandardColor(imageInfo.getAve());
        uploadPictureResult.setPicColor(standardColor);
        return uploadPictureResult;
    }

    /**
     * 封装返回结果（压缩图片 + 缩略图）
     *
     * @param originalFilename    原始图片名
     * @param compressedCiObject  压缩图
     * @param thumbnailCiObject   缩略图
     * @param originUrlUploadPath 原图上传路径
     * @param imageInfo           原图
     * @param file                原图文件
     * @return
     */
    private UploadPictureResult buildResult(String originalFilename, CIObject compressedCiObject
            , CIObject thumbnailCiObject, String originUrlUploadPath, ImageInfo imageInfo, File file) {
        // 如果原图是下载后可以直接查看的图片，也就是后缀为 "jpeg", "png", "jpg", "webp", "bmp"
        boolean checkResult = checkUrlSuffix(originUrlUploadPath);
        if (checkResult) {
            // 以原图为模板填充数据
            int picWidth = imageInfo.getWidth();
            int picHeight = imageInfo.getHeight();
            // 计算宽高比
            double picScale = NumberUtil.round(picWidth * 1.0 / picHeight, 2).doubleValue();
            // 封装返回结果
            UploadPictureResult uploadPictureResult = new UploadPictureResult();
            // 对象存储-原图路径
            uploadPictureResult.setUrl(cosClientConfig.getHost() + "/" + compressedCiObject.getKey());
            // 对象存储-缩略图路径
            uploadPictureResult.setThumbnailUrl(cosClientConfig.getHost() + "/" + thumbnailCiObject.getKey());
            // 对象存储-原图路径
            uploadPictureResult.setOriginUrl(cosClientConfig.getHost() + "/" + originUrlUploadPath);
            uploadPictureResult.setPicName(FileUtil.mainName(originalFilename));
            uploadPictureResult.setPicSize(FileUtil.size(file));
            uploadPictureResult.setPicWidth(picWidth);
            uploadPictureResult.setPicHeight(picHeight);
            boolean checkPicScaleResult = checkPicScale(picScale, uploadPictureResult);
            ThrowUtils.throwIf(checkPicScaleResult, ErrorCode.PARAMS_ERROR, "图片宽高比非法");
            uploadPictureResult.setPicScale(picScale);
            uploadPictureResult.setPicFormat(imageInfo.getFormat());
            String standardColor = ColorTransformUtils.getStandardColor(imageInfo.getAve());
            uploadPictureResult.setPicColor(standardColor);
            // 将是否可直接查看原图字段设置为 1，表示真
            uploadPictureResult.setOriginView(1);
            return uploadPictureResult;
        }
        // 以压缩图为模板填充数据
        int picWidth = compressedCiObject.getWidth();
        int picHeight = compressedCiObject.getHeight();
        // 计算宽高比
        double picScale = NumberUtil.round(picWidth * 1.0 / picHeight, 2).doubleValue();
        // 封装返回结果
        UploadPictureResult uploadPictureResult = new UploadPictureResult();
        // 对象存储-原图路径
        uploadPictureResult.setUrl(cosClientConfig.getHost() + "/" + compressedCiObject.getKey());
        // 对象存储-缩略图路径
        uploadPictureResult.setThumbnailUrl(cosClientConfig.getHost() + "/" + thumbnailCiObject.getKey());
        // 对象存储-原图路径
        uploadPictureResult.setOriginUrl(cosClientConfig.getHost() + "/" + originUrlUploadPath);
        uploadPictureResult.setPicName(FileUtil.mainName(originalFilename));
        uploadPictureResult.setPicSize(compressedCiObject.getSize().longValue());
        uploadPictureResult.setPicWidth(picWidth);
        uploadPictureResult.setPicHeight(picHeight);
        boolean checkPicScaleResult = checkPicScale(picScale, uploadPictureResult);
        ThrowUtils.throwIf(checkPicScaleResult, ErrorCode.PARAMS_ERROR, "图片宽高比非法");
        uploadPictureResult.setPicScale(picScale);
        uploadPictureResult.setPicFormat(compressedCiObject.getFormat().toUpperCase());
        String standardColor = ColorTransformUtils.getStandardColor(imageInfo.getAve());
        uploadPictureResult.setPicColor(standardColor);
        // 将是否可直接查看原图字段设置为 0，表示假
        uploadPictureResult.setOriginView(0);
        return uploadPictureResult;
    }

    /**
     * 检查 url 后缀是否能让图片直接展示
     *
     * @param url
     * @return
     */
    public boolean checkUrlSuffix(String url) {
        // 获取后缀前一个字符 "." 的索引
        int urlSuffixBeforeIndex = url.indexOf(".");
        if (urlSuffixBeforeIndex == -1) {
            // 等于-1，表面没找到正常后缀，直接返回false
            return false;
        } else {
            // 获取图片格式
            String urlSuffix = url.substring(urlSuffixBeforeIndex + 1);
            final List<String> ALLOW_SUFFIX_LIST = Arrays.asList("jpeg", "png", "jpg", "webp", "bmp");
            // 如果不包含正常后缀，返回false
            if (!ALLOW_SUFFIX_LIST.contains(urlSuffix)) {
                return false;
            }
        }
        return true;
    }

    public boolean checkPicScale(double picScale, UploadPictureResult uploadPictureResult) {
        // 校验宽高比是否合法
        if (picScale > 10.0 || picScale < 0.1) {
            String originUrl = uploadPictureResult.getOriginUrl();
            String url = uploadPictureResult.getUrl();
            String thumbnailUrl = uploadPictureResult.getThumbnailUrl();
            // 不合法删除对象存储的图片
            Picture picture = new Picture();
            picture.setOriginUrl(originUrl);
            picture.setUrl(url);
            picture.setThumbnailUrl(thumbnailUrl);
            pictureService.clearCosPictureFile(picture);
            return true;
        }
        return false;
    }

    /**
     * 清理临时文件
     *
     * @param file
     */
    public void deleteTempFile(File file) {
        if (file != null) {
            boolean deleteResult = file.delete();
            if (!deleteResult) {
                log.error("file delete error, filepath = {}", file.getAbsolutePath());
            }
        }
    }
}
